package cnpm02.j2ee.group3.logic.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.model.SelectItem;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.Conversational;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Identity;

import cnpm02.j2ee.group3.logic.dao.impl.CityDao;
import cnpm02.j2ee.group3.logic.dao.impl.EmailTemplateDao;
import cnpm02.j2ee.group3.logic.dao.impl.FlightLineDao;
import cnpm02.j2ee.group3.logic.dao.impl.PersonDao;
import cnpm02.j2ee.group3.logic.dao.impl.SoldTicketDao;
import cnpm02.j2ee.group3.logic.dao.impl.TicketDao;
import cnpm02.j2ee.group3.logic.dao.impl.TicketTypeDao;
import cnpm02.j2ee.group3.logic.exception.TicketNotSelectedYet;
import cnpm02.j2ee.group3.logic.utils.ATSMUtils;
import cnpm02.j2ee.group3.model.City;
import cnpm02.j2ee.group3.model.EmailTemplate;
import cnpm02.j2ee.group3.model.FlightLine;
import cnpm02.j2ee.group3.model.Person;
import cnpm02.j2ee.group3.model.SoldTicket;
import cnpm02.j2ee.group3.model.SoldTicketStatus;
import cnpm02.j2ee.group3.model.Ticket;
import cnpm02.j2ee.group3.model.TicketType;
import cnpm02.j2ee.group3.model.TripType;

@Name(TicketBooking.COMPONENT_NAME)
@Scope(ScopeType.CONVERSATION)
@Conversational
public class TicketBooking implements Serializable {
	private static final long serialVersionUID = 1L;
	private static final String STEP_SEARCH = "1";
	private static final String STEP_SEARCH_RESULT = "2";
	private static final String STEP_TICKET_DETAIL = "3";
	private static final String STEP_CONFIRMATION = "4";
	private static final String REGISTER_EMAIL_TEMPLATE_CODE = "BOO";
	
	public static final String COMPONENT_NAME = "ticketBooking";
	public static final String SEPERATOR = "#";
	@Logger
	private Log log;
	@In
	private ATSMUtils atsmUtils;
	@In
	private Identity identity;
	@In
	private CityDao cityDao;
	@In
	private TicketTypeDao ticketTypeDao;
	@In
	private TicketDao ticketDao;
	@In
	private FlightLineDao flightLineDao;
	@In
	private PersonDao personDao;
	@In
	private SoldTicketDao soldTicketDao;
	@In
	private EmailTemplateDao emailTemplateDao;
	
	
	private String currentStep;
	//Search parameter zone
	private List<SelectItem> tripKinds = new ArrayList<SelectItem>();
	private List<City> departCities = new ArrayList<City>();
	private List<City> arriveCities = new ArrayList<City>();
	
	private List<SelectItem> numbers = new ArrayList<SelectItem>();
	private List<TicketType> ticketTypes = new ArrayList<TicketType>();

	private List<FlightLine> flightLines = null;
	private Map<String, Map<String, TicketDTO>> departTicketGroupByDate = new HashMap<String, Map<String, TicketDTO>>();
	private Map<String, Map<String, TicketDTO>> returnTicketGroupByDate = new HashMap<String, Map<String, TicketDTO>>();
	
	private String selectedTripType = TripType.ROUND_WAY.toString();
	private String selectedFromCity;
	private String selectedToCity;
	private String selectedNumberOfPeople;
	private String selectedTicketType;
	
	private Date selectedDepartDate;
	private Date selectedReturnDate;
	private boolean returnDateRendered = true;
	
	//selected ticket zone
	private SoldTicket soldDepartTickket;
	private SoldTicket soldReturnTicket;
	private String selectedDepartTicketDTO;
	private String selectedReturnTicketDTO;
	
	public String getSelectedReturnTicketDTO() {
		return selectedReturnTicketDTO;
	}

	public void setSelectedReturnTicketDTO(String selectedReturnTicketDTO) {
		this.selectedReturnTicketDTO = selectedReturnTicketDTO;
	}

	private long sum;
	private long vatSum;
	private int serviceFee;
	//depart Ticket detail zone
	private int departVAT = 0;
	private long departSum = 0;
	private Ticket selectedDepartTicket;
	private TicketDTO departTicketDTO;
	
	//return Ticket detail zone
	private int returnVAT = 0;
	private long returnSum = 0;
	private Ticket selectedReturnTicket;
	private TicketDTO returnTicketDTO;
	
	@Create
	public void init() throws Exception {
		// Initialize days
		this.selectedDepartDate = new Date();
		this.selectedReturnDate = new Date();
	
		for (TicketType tt : ticketTypeDao.selectAll()) {
			ticketTypes.add(tt);
		}

		// Initialize city data
		if (flightLines == null) {
			flightLines = flightLineDao.selectAll();
		}

		for (FlightLine fl : flightLines) {
			if(!departCities.contains(fl.getFrom())) {
				this.departCities.add(fl.getFrom());
			}
		}
		selectedFromCity = departCities.get(0).getId().toString();// select by default

		for (FlightLine fl : flightLines) {
			if (selectedFromCity.equals(fl.getFrom().getId().toString())) {
				this.arriveCities.add(fl.getTo());
			}
		}
		// Init number of people
		for (int i = 1; i <= atsmUtils.getLastestRegulation()
				.getMaxPersonEachBookingTurn(); i++) {
			numbers.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
		}

		// Initialize Trip type
		for (TripType tt : TripType.values()) {
			tripKinds.add(new SelectItem(tt.toString(), tt
					.getNameByLanguage(atsmUtils.getCurrentLanguage())));
		}
		
		currentStep = STEP_SEARCH;
		
		soldDepartTickket = new SoldTicket();
		soldReturnTicket = new SoldTicket();
	}

	public void doChangeDepartCity() throws Exception {
		this.arriveCities.clear();
		for (FlightLine fl : flightLines) {
			if (selectedFromCity.equals(fl.getFrom().getId().toString())) {
				this.arriveCities.add(fl.getTo());
			}
		}
	}
	
	@Begin(join = true)
	public String search() throws Exception {
		if(soldDepartTickket == null) {
			return "search";
		}
		
		City from = cityDao.find(Long.valueOf(selectedFromCity));
		City to = cityDao.find(Long.valueOf(selectedToCity));
		
		List<Ticket> departTickets = ticketDao.selectWithinTicket(from, to,
				this.selectedDepartDate);
		
		fillTicketDTO(departTickets, departTicketGroupByDate);
		//Return
		if (isRoundTrip()) {
			List<Ticket> returnTickets = ticketDao.selectWithinTicket(to, from,
					this.selectedReturnDate);
			fillTicketDTO(returnTickets, returnTicketGroupByDate);
		}
		currentStep = STEP_SEARCH_RESULT; 
		return "searchResult";
	}
	
	public boolean isRoundTrip() {
		return TripType.valueOf(selectedTripType) == TripType.ROUND_WAY;
	}
	
	private void fillTicketDTO(List<Ticket> tickets, Map<String, Map<String, TicketDTO>> ticketGroupByDate) throws Exception {
		for(Ticket t : tickets) {
			String date = atsmUtils.stringOf(t.getFlight().getDepartDate(),
					"dd.MM.yy");
			String time = atsmUtils.stringOf(t.getFlight().getDepartDate(),
					"hh:mm");
			if (ticketGroupByDate.get(date) == null) {
				Map<String, TicketDTO> data = new HashMap<String, TicketDTO>();
				TicketDTO tDTO = new TicketDTO(date + SEPERATOR + time
						+ SEPERATOR + t.getId());
				tDTO.addTicket(t);
				data.put(time, tDTO);
				ticketGroupByDate.put(date, data);
			} else {
				if(ticketGroupByDate.get(date).get(time) == null) {
					TicketDTO tDTO = new TicketDTO(date + SEPERATOR + time
							+ SEPERATOR + t.getId());
					tDTO.addTicket(t);
					ticketGroupByDate.get(date).put(time, tDTO);
				} else {
					ticketGroupByDate.get(date).get(time).addTicket(t);
				}
			}
		}
	}

	public String nextToDetail() throws TicketNotSelectedYet {
		if(soldDepartTickket == null) {
			return "search";
		}
		//Key is store by date|time|ticketId 
		if (selectedDepartTicketDTO == null || isRoundTrip() ? (selectedReturnTicketDTO == null)
				: false) {
			throw new TicketNotSelectedYet();
		}
		
		//Depart
		String[] keys = selectedDepartTicketDTO.split(SEPERATOR);
		selectedDepartTicket = ticketDao.find(Long.valueOf(keys[2]));
		departTicketDTO = departTicketGroupByDate.get(keys[0]).get(keys[1]);
		departVAT = (int) (atsmUtils.getLastestRegulation().getVat() * getSelectedDepartTicket()
				.getPrice());
		serviceFee = atsmUtils.getLastestRegulation().getServiceFee();
		departSum = departVAT + serviceFee
				+ getSelectedDepartTicket().getPrice()
				* Integer.parseInt(selectedNumberOfPeople);
		//Return
		if(isRoundTrip()) {
			String[] keys1 = selectedReturnTicketDTO.split(SEPERATOR);
			selectedReturnTicket = ticketDao.find(Long.valueOf(keys1[2]));
			returnTicketDTO = returnTicketGroupByDate.get(keys1[0]).get(
					keys1[1]);
			returnVAT = (int) (atsmUtils.getLastestRegulation().getVat() * getSelectedReturnTicket()
					.getPrice());

			returnSum = returnVAT + serviceFee
					+ getSelectedReturnTicket().getPrice()
					* Integer.parseInt(selectedNumberOfPeople);
		}
		
		sum = departSum + returnSum;
		vatSum = departVAT + returnVAT;
		currentStep = STEP_TICKET_DETAIL;
		return "ticketDetail";
	}

	
	public String book() {
		if(soldDepartTickket == null) {
			return "search";
		}
		
		//Get current person
		String currentUser = identity.getCredentials().getUsername();
		Integer numberOfPeople = Integer.parseInt(selectedNumberOfPeople);
		Person p = (Person) personDao.find(currentUser);
		soldDepartTickket.setCustomer(p);
		soldDepartTickket.setTicket(selectedDepartTicket);
		soldDepartTickket.setQuantity(numberOfPeople);
		soldDepartTickket.setSoldDate(new Date());
		soldDepartTickket.setPrice(departSum);
		soldDepartTickket.setStatus(SoldTicketStatus.READY);
		soldTicketDao.saveOrUpdate(soldDepartTickket);
		
		if(isRoundTrip()) {
			soldReturnTicket.setCustomer(p);
			soldReturnTicket.setTicket(selectedReturnTicket);
			soldReturnTicket.setQuantity(numberOfPeople);
			soldReturnTicket.setSoldDate(new Date());
			soldReturnTicket.setPrice(returnSum);
			soldReturnTicket.setStatus(SoldTicketStatus.READY);
			soldTicketDao.saveOrUpdate(soldReturnTicket);
		}
		//Send mail
		//Set successful email
		log.debug("Sending mail...");
		EmailTemplate et = emailTemplateDao.getEmailTemplate(REGISTER_EMAIL_TEMPLATE_CODE,
				p.getLanguage().getCode());
		String subject = et.getSubject();
		String content = String.format(et.getContent(), p.getFirstname(), p.getFirstname(),
				p.getLogin());
		atsmUtils.sendMail(p.getEmail(), subject, content);
		
		log.debug("Create new person successfully!");
		currentStep = STEP_CONFIRMATION;
		return "confirm";
	}
	
	@End
	public String confirm() {
		return "home";
	}
	
	public List<SelectItem> getDepartCities() throws Exception {
		return translateCity(departCities);
	}

	public void setTripKinds(List<SelectItem> tripKinds) {
		this.tripKinds = tripKinds;
	}

	public List<SelectItem> getTripKinds() {
		tripKinds.clear();
		for (TripType tt : TripType.values()) {
			tripKinds.add(new SelectItem(tt.toString(), tt
					.getNameByLanguage(atsmUtils.getCurrentLanguage())));
		}
		return tripKinds;
	}
	
	public void setNumbers(List<SelectItem> numbers) {
		this.numbers = numbers;
	}

	public List<SelectItem> getNumbers() {
		return numbers;
	}

	public List<SelectItem> getTicketTypes() throws Exception {
		List<SelectItem> result = new ArrayList<SelectItem>();
		for (TicketType tt : ticketTypes) {
			result.add(new SelectItem(tt.getId(), atsmUtils.translate(tt)));
		}

		return result;
	}

	public void setSelectedFromCity(String selectedFromCity) {
		this.selectedFromCity = selectedFromCity;
	}

	public String getSelectedFromCity() {
		return selectedFromCity;
	}

	public void setSelectedToCity(String selectedToCity) {
		this.selectedToCity = selectedToCity;
	}

	public String getSelectedToCity() {
		return selectedToCity;
	}

	public void setSelectedTripType(String selectedTripType) {
		this.selectedTripType = selectedTripType;
	}

	public String getSelectedTripType() {
		return selectedTripType;
	}

	public void setSelectedNumberOfPeople(String selectedNumberOfPeople) {
		this.selectedNumberOfPeople = selectedNumberOfPeople;
	}

	public String getSelectedNumberOfPeople() {
		return selectedNumberOfPeople;
	}

	public void setSelectedTicketType(String selectedTicketType) {
		this.selectedTicketType = selectedTicketType;
	}

	public String getSelectedTicketType() {
		return selectedTicketType;
	}

	private List<SelectItem> translateCity(List<City> cities) throws Exception {
		List<SelectItem> result = new ArrayList<SelectItem>();
		for (City ci : cities) {
			result.add(new SelectItem(ci.getId(), atsmUtils.translate(ci)));
		}
		
		Collections.sort(result, new Comparator<SelectItem>() {

			public int compare(SelectItem o1, SelectItem o2) {
				return o1.getLabel().compareTo(o2.getLabel());
			}
			
		});
		return result;
	}
	
	public List<SelectItem> getArriveCities() throws Exception {
		return translateCity(arriveCities);
	}

	public void setDepartTicketGroupByDate(Map<String, Map<String, TicketDTO>> departTicketGroupByDate) {
		this.departTicketGroupByDate = departTicketGroupByDate;
	}

	public Map<String, Map<String, TicketDTO>> getDepartTicketGroupByDate() {
		return departTicketGroupByDate;
	}

	public String getSum() {
		return atsmUtils.formatMoney(sum);
	}

	public String getServiceFee() {
		return atsmUtils.formatMoney(serviceFee);
	}
	
	public String getDepartPrice() {
		return atsmUtils.formatMoney(getSelectedDepartTicket().getPrice());
	}
	
	public String getReturnPrice() {
		return atsmUtils.formatMoney(getSelectedReturnTicket().getPrice());
	}
	
	public String getDepartVat() {
		return atsmUtils.formatMoney(departVAT);
	}
	
	public String getSelectedDepartTicketDTO() {
		return selectedDepartTicketDTO;
	}
	
	public void setSelectedDepartTicketDTO(String selectedTicketDTO) {
		this.selectedDepartTicketDTO = selectedTicketDTO;
	}

	public TicketDTO getDepartTicketDTO() {
		return departTicketDTO;
	}

	public String getSelectedDepartTicketTypeOnDetail() throws Exception {
		return atsmUtils.translate(getSelectedDepartTicket().getTicketType());
	}
	
	public String getSelectedReturnTicketTypeOnDetail() throws Exception {
		return atsmUtils.translate(getSelectedReturnTicket().getTicketType());
	}

	public Ticket getSelectedDepartTicket() {
		return selectedDepartTicket;
	}

	public String getVatRate() {
		return String.valueOf(atsmUtils.getLastestRegulation().getVat() * 100)
				+ "%";
	}
	
	public void setReturnTicketGroupByDate(Map<String, Map<String, TicketDTO>> returnTicketGroupByDate) {
		this.returnTicketGroupByDate = returnTicketGroupByDate;
	}

	public Map<String, Map<String, TicketDTO>> getReturnTicketGroupByDate() {
		return returnTicketGroupByDate;
	}

	public boolean getReturnDateRendered() {
		return returnDateRendered;
	}

	public String getCurrentStep() {
		return currentStep;
	}

	public void setReturnTicket(SoldTicket returnTicket) {
		this.soldReturnTicket = returnTicket;
	}

	public SoldTicket getReturnTicket() {
		return soldReturnTicket;
	}

	public void setSelectedReturnTicket(Ticket selectedReturnTicket) {
		this.selectedReturnTicket = selectedReturnTicket;
	}

	public Ticket getSelectedReturnTicket() {
		return selectedReturnTicket;
	}

	public TicketDTO getReturnTicketDTO() {
		return returnTicketDTO;
	}

	public long getDepartSum() {
		return departSum;
	}

	public long getReturnSum() {
		return returnSum;
	}

	public String getReturnVat() {
		return atsmUtils.formatMoney(returnVAT);
	}

	public String getVatSum() {
		return atsmUtils.formatMoney(vatSum);
	}

	public void setSelectedDepartDate(Date selectedDepartDate) {
		this.selectedDepartDate = selectedDepartDate;
	}

	public Date getSelectedDepartDate() {
		return selectedDepartDate;
	}

	public void setSelectedReturnDate(Date selectedReturnDate) {
		this.selectedReturnDate = selectedReturnDate;
	}

	public Date getSelectedReturnDate() {
		return selectedReturnDate;
	}
}
